//=============================================================================
//
// Demonstrates coloring.
//
// Controls:
//		'A'/'D'/'W'/'S' - Rotate 
//
//=============================================================================

#include "ColoredCubeApp.h"

namespace D3D10
{
	LightingApp::LightingApp(const HINSTANCE hInstance)
		: D3DApp(hInstance)
		, mGrid(129, 129)
		, mEffect(0)
		, mTechnique(0)
		, mVertexLayout(0)
		, mEffectWVPVariable(0)
		, mRadius(100.0f)
	{
		D3DXMatrixIdentity(&mWorld);
		D3DXMatrixIdentity(&mView);
		D3DXMatrixIdentity(&mProjection);
		D3DXMatrixIdentity(&mWVP); 
	}
	
	LightingApp::~LightingApp()
	{
		if (md3dDevice)
			md3dDevice->ClearState();
	
		D3D10Utils::ReleaseCOM(mEffect);
		D3D10Utils::ReleaseCOM(mVertexLayout);
	}
	
	void LightingApp::initApp()
	{
		D3DApp::initApp();

		mGrid.init(md3dDevice, 1.0f, 1.0f);

		// Put the grid below of the camera.
		mWorld._42 = -50.0f;
	
		buildFX();
		buildVertexLayouts();
	}
	
	void LightingApp::onResize()
	{
		D3DApp::onResize();
	
		const float aspect = static_cast<float> (mClientWidth / mClientHeight);
		D3DXMatrixPerspectiveFovLH(&mProjection, D3D10Utils::PI * 0.25f, aspect, 1.0f, 1000.0f);
	}
	
	void LightingApp::updateScene(const float deltaTime)
	{
		D3DApp::updateScene(deltaTime);
	
		// Update camera position as a function of time.
		float x = mRadius * cosf(mTimer.getGameTime());
		float z = mRadius * sinf(mTimer.getGameTime());
		float y = 50.0f * sinf(mTimer.getGameTime()) + 100.f;

		// Build the view matrix.
		D3DXVECTOR3 pos(x, y, z);
		D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
		D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
		D3DXMatrixLookAtLH(&mView, &pos, &target, &up);
	}
	
	void LightingApp::drawScene()
	{
		D3DApp::drawScene();
	
		// Restore default states, input layout and primitive topology 
		// because mFont->DrawText changes them. 

		// Sets the depth-stencil state of the output-merger stage.
		md3dDevice->OMSetDepthStencilState(0, 0);
		
		// Set the blend state of the output-merger stage.
		const float blendFactors[] = {0.0f, 0.0f, 0.0f, 0.0f};
		md3dDevice->OMSetBlendState(0, blendFactors, 0xffffffff);

		// Bind an input-layout object to the input-assembler stage.
		md3dDevice->IASetInputLayout(mVertexLayout);

		// Bind information about the primitive type, 
		// and data order that describes input data for the input assembler stage.
		md3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	
		mWVP = mWorld * mView * mProjection;
		mEffectWVPVariable->SetMatrix(reinterpret_cast<float *> (&mWVP));
	
		D3D10_TECHNIQUE_DESC techDesc;
		mTechnique->GetDesc(&techDesc);
		for(UINT p = 0; p < techDesc.Passes; ++p)
		{
			// Set the state contained in a pass to the device.
			mTechnique->GetPassByIndex(p)->Apply(0);
			mGrid.draw();
		}
	
		// We specify DT_NOCLIP, so we do not care about width/height of the rect.
		RECT R = {5, 5, 0, 0};
		mFont->DrawText(0, mFrameStats.c_str(), -1, &R, DT_NOCLIP, D3D10Utils::BLACK);
	
		mSwapChain->Present(0, 0);
	}
	
	void LightingApp::buildFX()
	{
		DWORD shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
	#if defined( DEBUG ) || defined( _DEBUG )
		shaderFlags |= D3D10_SHADER_DEBUG;
		shaderFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
	#endif
	
		// Create the effect from a file.
		ID3D10Blob* compilationErrors = 0;
		HRESULT hr = 0;
		hr = D3DX10CreateEffectFromFile(L"color.fx", 0, 0, 
			"fx_4_0", shaderFlags, 0, md3dDevice, 0, 0, &mEffect, &compilationErrors, 0);
		if (FAILED(hr))
		{
			if(compilationErrors)
			{
				MessageBoxA(0, reinterpret_cast<char *> (compilationErrors->GetBufferPointer()), 0, 0);
				D3D10Utils::ReleaseCOM(compilationErrors);
			}

			DXTrace(__FILE__, (DWORD)__LINE__, hr, L"D3DX10CreateEffectFromFile", true);
		} 
	
		// Get the technique and the WorldViewProjection Matrix from the effect file.
		mTechnique = mEffect->GetTechniqueByName("ColorTech");		
		mEffectWVPVariable = mEffect->GetVariableByName("gWVP")->AsMatrix();
	}
	
	void LightingApp::buildVertexLayouts()
	{
		// Create the vertex input layout.
		// A description of a single element for the input-assembler stage.
		D3D10_INPUT_ELEMENT_DESC vertexDesc[] =
		{
			{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
			{"COLOR",    0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0}
		};
	
		// Create an input-layout object to describe the 
		// input-buffer data for the input-assembler stage.
		D3D10_PASS_DESC PassDesc;
		 
		// Fill the pass description with the only pass that we have in
		// the current effect file.
		mTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);

		// PassDesc.pIAInputSignature is a pointer to the compilder shader that
		// contains a input signature which is validated against the array of elements (vertexDesc).
		// A shader signature is a list of the parameters that are either input to or 
		// output from a shader function. In Direct3D 10, adjacent stages effectively 
		// share a register array, where the output shader (or pipeline stage) writes 
		// data to specific locations in the register array and the input shader must 
		// read from the same locations. The API uses shader signatures to bind shader 
		// outputs with inputs without the overhead of semantic resolution. 
		// In Direct3D 10, input signatures are generated from a shader-input declaration 
		// and the output signature is generated from a shader-output declaration.
		D3D10Utils::ErrorHandler(md3dDevice->CreateInputLayout(vertexDesc, 2, PassDesc.pIAInputSignature,
			PassDesc.IAInputSignatureSize, &mVertexLayout));
	}
}
